#include "pch.h"
#include "ReaderWriter.h"

using namespace Microsoft::WRL;
using namespace Windows::Storage;
using namespace Windows::Storage::FileProperties;
using namespace Windows::Storage::Streams;
using namespace Windows::Foundation;
using namespace Windows::ApplicationModel;
using namespace concurrency;

ReaderWriter::ReaderWriter()
{
   m_location     = Package::Current->InstalledLocation;
   m_locationPath = Platform::String::Concat(m_location->Path, "\\");
}


ReaderWriter::ReaderWriter(
   _In_ Windows::Storage::StorageFolder ^ folder
   )
{
   m_location = folder;
   Platform::String ^ path = m_location->Path;
   if (path->Length() == 0)
   {
      // Applications are not permitted to access certain
      // folders, such as the Documents folder, using this
      // code path.  In such cases, the Path property for
      // the folder will be an empty string.
      throw ref new Platform::FailureException();
   }
   m_locationPath = Platform::String::Concat(path, "\\");
}


Platform::Array<byte> ^ ReaderWriter::ReadData(
   _In_ Platform::String ^ filename
   )
{
   CREATEFILE2_EXTENDED_PARAMETERS extendedParams = { 0 };

   extendedParams.dwSize               = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
   extendedParams.dwFileAttributes     = FILE_ATTRIBUTE_NORMAL;
   extendedParams.dwFileFlags          = FILE_FLAG_SEQUENTIAL_SCAN;
   extendedParams.dwSecurityQosFlags   = SECURITY_ANONYMOUS;
   extendedParams.lpSecurityAttributes = nullptr;
   extendedParams.hTemplateFile        = nullptr;

   Wrappers::FileHandle file(
      CreateFile2(
         Platform::String::Concat(m_locationPath, filename)->Data(),
         GENERIC_READ,
         FILE_SHARE_READ,
         OPEN_EXISTING,
         &extendedParams
         )
      );
   if (file.Get() == INVALID_HANDLE_VALUE)
   {
      throw ref new Platform::FailureException();
   }

   FILE_STANDARD_INFO fileInfo = { 0 };
   if (!GetFileInformationByHandleEx(
          file.Get(),
          FileStandardInfo,
          &fileInfo,
          sizeof(fileInfo)
          ))
   {
      throw ref new Platform::FailureException();
   }

   if (fileInfo.EndOfFile.HighPart != 0)
   {
      throw ref new Platform::OutOfMemoryException();
   }

   Platform::Array<byte> ^ fileData = ref new Platform::Array<byte>(fileInfo.EndOfFile.LowPart);

   if (!ReadFile(
          file.Get(),
          fileData->Data,
          fileData->Length,
          nullptr,
          nullptr
          ))
   {
      throw ref new Platform::FailureException();
   }

   return(fileData);
}

task<Platform::Array<byte> ^> ReaderWriter::ReadDataAsync(
   _In_ Platform::String ^ filename
   )
{
   return(task<StorageFile ^>(m_location->GetFileAsync(filename)).then([ = ](StorageFile ^ file)
                                                                       {
                                                                          return FileIO::ReadBufferAsync(file);
                                                                       }
                                                                       ).then([ = ](IBuffer ^ buffer)
                                                                              {
                                                                                 auto fileData = ref new Platform::Array<byte>(buffer->Length);
                                                                                 DataReader::FromBuffer(buffer)->ReadBytes(fileData);
                                                                                 return fileData;
                                                                              }
                                                                              ));
}


uint32 ReaderWriter::WriteData(
   _In_ Platform::String ^            filename,
   _In_ const Platform::Array<byte> ^ fileData
   )
{
   CREATEFILE2_EXTENDED_PARAMETERS extendedParams = { 0 };

   extendedParams.dwSize               = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
   extendedParams.dwFileAttributes     = FILE_ATTRIBUTE_NORMAL;
   extendedParams.dwFileFlags          = FILE_FLAG_SEQUENTIAL_SCAN;
   extendedParams.dwSecurityQosFlags   = SECURITY_ANONYMOUS;
   extendedParams.lpSecurityAttributes = nullptr;
   extendedParams.hTemplateFile        = nullptr;

   Wrappers::FileHandle file(
      CreateFile2(
         Platform::String::Concat(m_locationPath, filename)->Data(),
         GENERIC_WRITE,
         0,
         CREATE_ALWAYS,
         &extendedParams
         )
      );
   if (file.Get() == INVALID_HANDLE_VALUE)
   {
      throw ref new Platform::FailureException();
   }

   DWORD numBytesWritten;
   if (
      !WriteFile(
         file.Get(),
         fileData->Data,
         fileData->Length,
         &numBytesWritten,
         nullptr
         ) ||
      (numBytesWritten != fileData->Length)
      )
   {
      throw ref new Platform::FailureException();
   }

   return(numBytesWritten);
}


task<void> ReaderWriter::WriteDataAsync(
   _In_ Platform::String ^            filename,
   _In_ const Platform::Array<byte> ^ fileData
   )
{
   return(task<StorageFile ^>(m_location->CreateFileAsync(filename, CreationCollisionOption::ReplaceExisting)).then([ = ](StorageFile ^ file)
                                                                                                                    {
                                                                                                                       FileIO::WriteBytesAsync(file, fileData);
                                                                                                                    }
                                                                                                                    ));
}
